package com.jiankangjin.trading.dao.utils;

import com.jiankangjin.common.lang.Assert;
import com.jiankangjin.common.lang.ObjectUtils;
import com.jiankangjin.common.lang.StringUtils;
import com.jiankangjin.common.util.CollectionUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 该类主要用于在DBunit进行测试时，为了简化自己构建实体对象的麻烦，特此编写了一个简单的将DBunit的测试数据文件转为Java对象。
 * 该类仅仅用于测试，在性能和封装上没有进行较好的测试。
 * Created by mzllon on 2014/7/9.
 */
public class DatasetConvertBeans {

    private Element root;

    /**
     * 构造器，
     * @param xmlFilename dataset的测试数据文件
     */
    public DatasetConvertBeans(String xmlFilename) {
        try {
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(
                    DatasetConvertBeans.class.getClassLoader().getResourceAsStream(xmlFilename)
            );
            Element root = document.getRootElement();

            this.root = root;

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    private List<Element> parsetXML(String tableName,Object[] attrNames, Object[] attrValues) {
        Assert.isNotNull(tableName);
        if (ObjectUtils.isEmpty(attrNames) || ObjectUtils.isEmpty(attrValues)) {
            return root.elements();
        }
        if (attrNames.length != attrValues.length) {
            throw new RuntimeException("XML的属性名个数和属性值个数不一致！");
        }
        StringBuilder xpath = new StringBuilder("/dataset/");
        xpath.append(tableName);
        for (int i = 0; i < attrNames.length; i++) {
            Object attrname = attrNames[i];
            Object attrValue = attrValues[i];
            xpath.append("[@").append(attrname)
                    .append("='").append(attrValue).append("']");
        }

        System.out.println(xpath.toString());
        List<Element> selectNodes = this.root.selectNodes(xpath.toString());
        return selectNodes;
    }
    
    public <T> T toSingleBean(String tableName,String attrName, Object attrValue,Class<T> used2Clz){
    	return toSingleBean(tableName, new Object[]{attrName}, new Object[]{attrValue}, used2Clz);
    }

    /**
     * 将XML的某个节点转为一个单一的实体对象
     * @param tableName
     * @param attrNames
     * @param attrValues
     * @param used2Clz
     * @param <T>
     * @return
     */
    public <T> T toSingleBean(String tableName,Object[] attrNames, Object[] attrValues,Class<T> used2Clz) {

        List<Element> selectNodes = this.parsetXML(tableName, attrNames, attrValues);
        if (CollectionUtils.isEmpty(selectNodes)) {
            return null;
        }

        if (selectNodes.size() > 1) {
            throw new RuntimeException("XML搜索的结果超过一个！");
        }

        try {
            return xmlNode2Bean(selectNodes.get(0), used2Clz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 根据搜索条件得到的节点集合转为Java集合对象
     * @param tableName
     * @param attrNames
     * @param attrValues
     * @param used2Clz
     * @param <T>
     * @return
     */
    public <T> List<T> toBeans(String tableName, Object[] attrNames, Object[] attrValues, Class<T> used2Clz) {
        List<Element> selectNodes = this.parsetXML(tableName, attrNames, attrValues);
        if (CollectionUtils.isEmpty(selectNodes)) {
            return null;
        }

        List<T> list = new ArrayList<T>(selectNodes.size() >> 2);
        for (Element node : selectNodes) {
            try {
                list.add(xmlNode2Bean(node, used2Clz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    private <T> T xmlNode2Bean(Element node, Class<T> used2Clz) throws Exception {
        List<Attribute> attrs = node.attributes();
//        List<String> cloneAttrs = new ArrayList<String>(attrs.size() >> 2);
        Map<String, Attribute> map = new HashMap<String, Attribute>(attrs.size() >> 2);
        for (Attribute attr : attrs) {
            String an = attr.getName().replace("_", "");

            System.out.println(attr.getName());
            System.out.println(attr.getValue());

            map.put(an, attr);
        }

        Method[] methods = used2Clz.getDeclaredMethods();
        T entity = used2Clz.newInstance();
        for (Method method : methods) {
           /* String methodName = method.getName();
            if (methodName.startsWith("set")) {
                String fieldName = StringUtils.uncapitalize(methodName.substring(3));
                System.out.println(fieldName);
                for (String attr : map.keySet()) {
                    if (attr.equalsIgnoreCase(fieldName)) {
                        String value = map.get(attr).getValue();
                        method.invoke(entity, value);
                    }
                }
            }*/
            invokeValue(method, map, entity);
        }

        return entity;
    }

    private void invokeValue(Method method, Map<String, Attribute> datasetMap,Object obj) throws Exception{
        String methodName = method.getName();
        if (methodName.startsWith("set")) {
            String fieldName = StringUtils.uncapitalize(methodName.substring(3));

            Class<?>[] parameterTypeClasses = method.getParameterTypes();
            Class<?> parameterType = parameterTypeClasses[0];

            for (String attr : datasetMap.keySet()) {
                if (attr.equalsIgnoreCase(fieldName)) {
                    String attrValue = datasetMap.get(attr).getValue();
                    if (parameterType == Short.class) {
                        method.invoke(obj, Short.parseShort(attrValue));
                    } else if (parameterType == Character.class) {
                        method.invoke(obj, attrValue.toCharArray()[0]);
                    } else if (parameterType == Byte.class) {
                        method.invoke(obj, Byte.parseByte(attrValue));
                    } else if (parameterType == Integer.class) {
                        method.invoke(obj, Integer.valueOf(attrValue));
                    } else if (parameterType == Float.class) {
                        method.invoke(obj, Float.valueOf(attrValue));
                    } else if (parameterType == Double.class) {
                        method.invoke(obj, Double.valueOf(attrValue));
                    } else if (parameterType == Boolean.class) {
                        method.invoke(obj, Boolean.valueOf(attrValue));
                    } else if(parameterType == String.class) {
                        method.invoke(obj, (String) attrValue);
                    } else {
                        method.invoke(obj, attrValue);
                    }
                    break;
                }
            }
        }

    }

}
